import subprocess
import tempfile
import os
import asyncio
from datetime import datetime, timedelta
from fastapi import FastAPI, Depends, HTTPException, status, WebSocket, WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from typing import List
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse

from . import crud, schemas, auth, models
from .database import SessionLocal, engine, get_db

# 添加 timedelta 导入
from datetime import timedelta

models.Base.metadata.create_all(bind=engine)

app = FastAPI(title="脚本管理平台",
    description="基于 FastAPI 的脚本管理平台",
    version="1.0.0",
    docs_url=None,
    redoc_url=None,)

# 挂载静态文件目录 - 前端文件
app.mount("/static", StaticFiles(directory="static"), name="static")
app.mount("/assets", StaticFiles(directory="static/assets"), name="assets")

# CORS配置
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://10.0.172.53:3000", "http://10.0.172.53:8080"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# WebSocket连接管理器
class ConnectionManager:
    def __init__(self):
        self.active_connections: List[WebSocket] = []

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)

    def disconnect(self, websocket: WebSocket):
        self.active_connections.remove(websocket)

    async def send_personal_message(self, message: str, websocket: WebSocket):
        await websocket.send_text(message)

    async def broadcast(self, message: str):
        for connection in self.active_connections:
            await connection.send_text(message)

manager = ConnectionManager()

# 提供前端入口文件
@app.get("/", include_in_schema=False)
async def read_index():
    return FileResponse("static/index.html")

#@app.get("/{full_path:path}", include_in_schema=False)
#async def serve_frontend(full_path: str):
    # 检查请求的路径是否对应静态文件
#    static_path = f"static/{full_path}"
#    if os.path.exists(static_path) and os.path.isfile(static_path):
#        return FileResponse(static_path)

    # 如果是 API 请求，返回 404
#    if full_path.startswith("api/"):
#        raise HTTPException(status_code=404, detail="API endpoint not found")

    # 其他情况返回前端入口文件（支持前端路由）
#    return FileResponse("static/index.html")

@app.post("/token", response_model=schemas.Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
    user = auth.authenticate_user(db, form_data.username, form_data.password)

    # 添加详细的调试信息
    print(f"认证结果: {user}")
    print(f"认证结果类型: {type(user)}")

    if not user:
        print(f"认证失败，用户: {form_data.username}")  # 调试信息
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户名或密码错误",
            headers={"WWW-Authenticate": "Bearer"},
        )

    # 确保 user 是 User 对象而不是布尔值
    if hasattr(user, 'username'):
        access_token_expires = timedelta(minutes=auth.config.settings.ACCESS_TOKEN_EXPIRE_MINUTES)
        access_token = auth.create_access_token(
            data={"sub": user.username}, expires_delta=access_token_expires
        )
        return {"access_token": access_token, "token_type": "bearer"}
    else:
        print(f"意外的用户对象类型: {type(user)}")  # 调试信息
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="认证过程中出现错误",
            headers={"WWW-Authenticate": "Bearer"},
        )

@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
    db_user = crud.get_user_by_username(db, username=user.username)
    if db_user:
        raise HTTPException(status_code=400, detail="Username already registered")
    db_user = crud.get_user_by_email(db, email=user.email)
    if db_user:
        raise HTTPException(status_code=400, detail="Email already registered")
    return crud.create_user(db=db, user=user)

@app.get("/users/me/", response_model=schemas.User)
async def read_users_me(current_user: schemas.User = Depends(auth.get_current_active_user)):
    return current_user

@app.post("/scripts/", response_model=schemas.Script)
def create_script(
    script: schemas.ScriptCreate,
    db: Session = Depends(get_db),
    current_user: schemas.User = Depends(auth.get_current_active_user)
):
    return crud.create_script(db=db, script=script, user_id=current_user.id)

@app.get("/scripts/", response_model=List[schemas.Script])
def read_scripts(
    skip: int = 0, 
    limit: int = 100, 
    db: Session = Depends(get_db),
    current_user: schemas.User = Depends(auth.get_current_active_user)
):
    if current_user.is_superuser:
        scripts = crud.get_scripts(db, skip=skip, limit=limit)
    else:
        scripts = crud.get_user_scripts(db, user_id=current_user.id, skip=skip, limit=limit)
    return scripts

@app.get("/scripts/{script_id}", response_model=schemas.Script)
def read_script(
    script_id: int,
    db: Session = Depends(get_db),
    current_user: schemas.User = Depends(auth.get_current_active_user)
):
    db_script = crud.get_script(db, script_id=script_id)
    if db_script is None:
        raise HTTPException(status_code=404, detail="Script not found")
    if not current_user.is_superuser and db_script.owner_id != current_user.id:
        raise HTTPException(status_code=403, detail="Not enough permissions")
    return db_script

@app.put("/scripts/{script_id}", response_model=schemas.Script)
def update_script(
    script_id: int,
    script: schemas.ScriptUpdate,
    db: Session = Depends(get_db),
    current_user: schemas.User = Depends(auth.get_current_active_user)
):
    db_script = crud.get_script(db, script_id=script_id)
    if db_script is None:
        raise HTTPException(status_code=404, detail="Script not found")
    if not current_user.is_superuser and db_script.owner_id != current_user.id:
        raise HTTPException(status_code=403, detail="Not enough permissions")
    return crud.update_script(db=db, script_id=script_id, script=script)

@app.delete("/scripts/{script_id}")
def delete_script(
    script_id: int,
    db: Session = Depends(get_db),
    current_user: schemas.User = Depends(auth.get_current_active_user)
):
    db_script = crud.get_script(db, script_id=script_id)
    if db_script is None:
        raise HTTPException(status_code=404, detail="Script not found")
    if not current_user.is_superuser and db_script.owner_id != current_user.id:
        raise HTTPException(status_code=403, detail="Not enough permissions")
    crud.delete_script(db=db, script_id=script_id)
    return {"message": "Script deleted successfully"}

def is_safe_script_content(content: str, language: str) -> bool:
    """检查脚本内容是否安全"""
    dangerous_patterns = [
        "rm -rf /",  # 危险的删除命令
        "dd if=",    # 磁盘操作
        "mkfs",      # 文件系统操作
        ":(){ :|:& };:",  # fork 炸弹
        # 添加更多危险模式...
    ]

    for pattern in dangerous_patterns:
        if pattern in content:
            return False

    return True

@app.post("/scripts/{script_id}/execute")
async def execute_script(
    script_id: int,
    db: Session = Depends(get_db),
    current_user: schemas.User = Depends(auth.get_current_active_user)
):
    # Get script
    db_script = crud.get_script(db, script_id=script_id)
    if db_script is None:
        raise HTTPException(status_code=404, detail="Script not found")
    if not current_user.is_superuser and db_script.owner_id != current_user.id:
        raise HTTPException(status_code=403, detail="Not enough permissions")

    # 安全检查
    if not is_safe_script_content(db_script.content, db_script.language):
        raise HTTPException(
            status_code=400,
            detail="脚本内容包含潜在危险操作，无法执行"
        )

    # Create execution log
    execution_log = crud.create_execution_log(
        db=db,
        execution_log=schemas.ExecutionLogCreate(script_id=script_id),
        user_id=current_user.id
    )

    # Execute script in background
    asyncio.create_task(execute_script_background(db_script, execution_log.id, db))

    return {
        "execution_id": execution_log.id,
        "message": "Script execution started"
    }


async def execute_script_background(script, execution_log_id: int, db: Session):
    try:
        # 更新状态为运行中
        crud.update_execution_log(
            db=db,
            log_id=execution_log_id,
            execution_log=schemas.ExecutionLogUpdate(status="running")
        )
        
        # 根据脚本语言决定执行方式
        if script.language == "python":
            # Python 脚本执行逻辑
            with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
                f.write(script.content)
                temp_script_path = f.name
            
            # 执行 Python 脚本
            result = subprocess.run(
                ['python', temp_script_path],
                capture_output=True,
                text=True,
                timeout=300  # 5分钟超时
            )
            
        elif script.language == "shell":
            # Shell 脚本执行逻辑
            with tempfile.NamedTemporaryFile(mode='w', suffix='.sh', delete=False) as f:
                f.write("#!/bin/bash\n")
                f.write(script.content)
                temp_script_path = f.name
            
            # 设置执行权限
            os.chmod(temp_script_path, 0o755)
            
            # 执行 Shell 脚本
            result = subprocess.run(
                ['bash', temp_script_path],
                capture_output=True,
                text=True,
                timeout=300  # 5分钟超时
            )
            
        else:
            # 不支持的语言
            raise ValueError(f"不支持的脚本语言: {script.language}")
        
        # 更新执行日志
        if result.returncode == 0:
            status = "success"
            output = result.stdout
            error_message = None
        else:
            status = "failed"
            output = result.stdout
            error_message = result.stderr
        
        # 清理临时文件
        if os.path.exists(temp_script_path):
            os.unlink(temp_script_path)
        
    except subprocess.TimeoutExpired:
        status = "failed"
        output = None
        error_message = "脚本执行超时"
    except Exception as e:
        status = "failed"
        output = None
        error_message = str(e)
    
    # 更新执行日志结果
    crud.update_execution_log(
        db=db,
        log_id=execution_log_id,
        execution_log=schemas.ExecutionLogUpdate(
            status=status,
            output=output,
            error_message=error_message,
        )
    )


@app.get("/execution-logs/", response_model=List[schemas.ExecutionLog])
def read_execution_logs(
    skip: int = 0, 
    limit: int = 100, 
    db: Session = Depends(get_db),
    current_user: schemas.User = Depends(auth.get_current_active_user)
):
    if current_user.is_superuser:
        execution_logs = crud.get_execution_logs(db, skip=skip, limit=limit)
    else:
        execution_logs = crud.get_user_execution_logs(db, user_id=current_user.id, skip=skip, limit=limit)
    return execution_logs

@app.get("/execution-logs/{log_id}", response_model=schemas.ExecutionLog)
def read_execution_log(
    log_id: int,
    db: Session = Depends(get_db),
    current_user: schemas.User = Depends(auth.get_current_active_user)
):
    execution_log = crud.get_execution_log(db, log_id=log_id)
    if execution_log is None:
        raise HTTPException(status_code=404, detail="Execution log not found")
    if not current_user.is_superuser and execution_log.user_id != current_user.id:
        raise HTTPException(status_code=403, detail="Not enough permissions")
    return execution_log

@app.websocket("/ws/execution-logs/{log_id}")
async def websocket_endpoint(websocket: WebSocket, log_id: int, db: Session = Depends(get_db)):
    await manager.connect(websocket)
    try:
        while True:
            # 定期检查执行状态并发送更新
            execution_log = crud.get_execution_log(db, log_id=log_id)
            if execution_log:
                await websocket.send_json({
                    "status": execution_log.status,
                    "output": execution_log.output,
                    "error_message": execution_log.error_message
                })
            
            # 如果执行完成，停止发送更新
            if execution_log and execution_log.status in ["success", "failed"]:
                break
                
            await asyncio.sleep(1)  # 每秒检查一次
    except WebSocketDisconnect:
        manager.disconnect(websocket)

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)
